﻿//-----------------------------------------------------------------------
// <copyright file="ServicesBuilder.cs" company="SLWY Enterprises">
// * Copyright (C) 2017 四川商旅无忧科技有限公司 版权所有。
// * version : 1.0
// * author  : qinchaoyue
// * FileName: ServicesBuilder.cs
// * history : created by qinchaoyue 2017-10-16 11:46:26
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Linq;
using Autofac;
using Autofac.Extensions.DependencyInjection;

namespace Slwy.FrameCore.Dependency
{
    using AutoMapper;

    using Mapping;

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;

    using Slwy.Context;
    using Slwy.JwtAuth;
    using Slwy.Logger;
    using Slwy.Utility.Extensions;

    /// <summary>
    /// The services builder.
    /// </summary>
    public class ServicesBuilder
    {
        /// <summary>
        /// 框架配置
        /// </summary>
        /// <param name="app">the app. </param>
        /// <param name="env">the env . </param>
        public static void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.Use(
                async (context, next) =>
                    {
                        var token = context.Request.Headers["Authorization"].ToString();
                        if (!token.IsNullOrEmpty())
                        {
                            SlwyContext.Current.SetToken(token.Replace("slwyjwt ", string.Empty));
                            var payload = JwtTools.GetJwtPayload(token);
                            if (payload != null)
                            {
                                SlwyContext.Current.SetOperator(
                                    new Operator
                                    {
                                        UserId = payload.UID,
                                        UserName = payload.UserName,
                                        DepartmentId = payload.DeptID,
                                        CompanyId = payload.CompanyID,
                                        Content = payload.Content, 
                                        UserAccount = payload.UName,
                                        KeyId = Guid.NewGuid()
                                    });
                            }
                        }
                        else
                        {
                            SlwyContext.Current.SetOperator(
                                new Operator
                                    {
                                        KeyId = Guid.NewGuid()
                                    });
                        }

                        await next();
                        ContextManager.AutoDelete();
                    });

            // 使用一个静态变量保存注入的容器, 方便获取容器中的服务
            ServiceLocator.Instance = app.ApplicationServices;
        }

        /// <summary>
        /// The build.
        /// </summary>
        /// <param name="services">
        /// The services.
        /// </param>
        /// <param name="configuration">
        /// The configuration.
        /// </param>
        /// <returns>
        /// The <see cref="IServiceProvider"/>.
        /// </returns>
        public IServiceProvider Build(Microsoft.Extensions.DependencyInjection.IServiceCollection services, IConfiguration configuration)
        {
            // 添加局部生命周期类型的映射
            return this.RegisterAutofac(services, configuration);
        }

        /// <summary>
        /// The register autofac.
        /// </summary>
        /// <param name="services">
        /// The services.
        /// </param>
        /// <param name="configuration">
        /// The configuration.
        /// </param>
        /// <returns>
        /// The <see cref="IServiceProvider"/>.
        /// </returns>
        private IServiceProvider RegisterAutofac(Microsoft.Extensions.DependencyInjection.IServiceCollection services, IConfiguration configuration)
        {
            var autoFacServices = new ServiceCollection();
            var builder = new ContainerBuilder();
            this.InitContext(configuration);
            services.AddSingleton(configuration);

            // 添加 AutoMapper 单利生命周期
            services.AddAutoMapper();

            // 扫描整个程序集, 获取 AutoMapper 配置并注册
            services.AddAutoMapperClasses();

            // 注册日志服务
            services.AddLoggerService(configuration);

            ServiceBuildOptions options = new ServiceBuildOptions();

            // 添加局部生命周期类型的映射
            var dependencyTypes = options.ScopeTypeFinder.FindAll();
            this.AddTypeWithInterfaces(autoFacServices, dependencyTypes, LifetimeStyle.Scoped);

            // 添加单例生命周期类型的映射
            var singletonTypes = options.SingletonTypeFinder.FindAll();
            this.AddTypeWithInterfaces(autoFacServices, singletonTypes, LifetimeStyle.Singleton);

            var transTypes = options.TransTypeFinder.FindAll();
            this.AddTypeWithInterfaces(autoFacServices, transTypes, LifetimeStyle.Transient);

            builder.Populate(autoFacServices);
            builder.Populate(services);

            var containerBuilder = builder.Build();
            return new AutofacServiceProvider(containerBuilder);
        }

        /// <summary>
        /// 初始化上下文环境
        /// </summary>
        /// <param name="configuration">配置项</param>
        private void InitContext(IConfiguration configuration)
        {
            var fileName = "appsettings.json";
            var environment = configuration.GetSection("environment").Value;
            SlwyContext.Current.SetEnvironment(environment);
            SlwyContext.Current.SetSettingName(fileName.Insert(fileName.IndexOf('.'), $".{environment}"));
        }


        /// <summary>
        /// 以类型实现的接口进行服务添加，需排除
        /// <see cref="ITransientDependency"/>、
        /// <see cref="IScopeDependency"/>、
        /// <see cref="ISingletonDependency"/>、
        /// <see cref="IDependency"/>、
        /// <see cref="IDisposable"/>等非业务接口，如无接口则注册自身
        /// </summary>
        /// <param name="services">服务映射信息集合</param>
        /// <param name="implementationTypes">要注册的实现类型集合</param>
        /// <param name="lifetime">注册的生命周期类型</param>
        protected virtual void AddTypeWithInterfaces(IServiceCollection services, Type[] implementationTypes, LifetimeStyle lifetime)
        {
            foreach (Type implementationType in implementationTypes)
            {
                if (implementationType.IsAbstract || implementationType.IsInterface)
                {
                    continue;
                }
                Type[] interfaceTypes = GetImplementedInterfaces(implementationType);
                if (interfaceTypes.Length == 0)
                {
                    services.Add(implementationType, implementationType, lifetime);
                    continue;
                }
                foreach (Type interfaceType in interfaceTypes)
                {
                    services.Add(interfaceType, implementationType, lifetime);
                }
            }
        }

        /// <summary>
        /// The get implemented interfaces.
        /// </summary>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <returns>
        /// The <see cref="Type[]"/>.
        /// </returns>
        private static Type[] GetImplementedInterfaces(Type type)
        {
            Type[] exceptInterfaces =
            {
                typeof(IDisposable),
                typeof(IDependency),
                typeof(ITransientDependency),
                typeof(IScopeDependency),
                typeof(ISingletonDependency)
            };
            var interfaceTypes = type.GetInterfaces().Where(m => !exceptInterfaces.Contains(m)).ToArray();
            for (var index = 0; index < interfaceTypes.Length; index++)
            {
                var interfaceType = interfaceTypes[index];
                if (interfaceType.IsGenericType && !interfaceType.IsGenericTypeDefinition && interfaceType.FullName == null)
                {
                    interfaceTypes[index] = interfaceType.GetGenericTypeDefinition();
                }
            }

            return interfaceTypes;
        }
    }
}
